package org.weasel.data;

import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.jdom.xpath.XPath;

public class XMLManager {
	
	private String xmlFile;
	private Document doc;
	
	/**
	 * 
	 * @param xmlFile name of the file which is managed by the XMLManager
	 * @throws Exception
	 */
	public XMLManager(String xmlFile) throws Exception{
		this.xmlFile = xmlFile;
		if(new FileManager().existsFile(xmlFile)){
			loadXML();
		}
	}
	
	/**
	 * Loads the file into the Document object if the xml-file
	 * is present
	 * @throws Exception
	 */
	private void loadXML() throws Exception{
		SAXBuilder builder = new SAXBuilder();
		try {
			doc = builder.build(xmlFile);
		} catch (JDOMException e) {
			throw e;
		} catch (IOException e) {
			throw e;
		}		
	}
	
	public void reloadXMLDocument() throws Exception {
		if(new FileManager().existsFile(xmlFile)){
			loadXML();
		}
	}
	
	/**
	 * Adds the root element to the Document object. This is needed in the case
	 * We create a new XML-file where no root is 
	 * @param name
	 */
	public void createRootElement(String name){
		Element root = new Element(name);
		doc = new Document(root);
	}
	
	public Element getRootElement(){
		return doc.getRootElement();
	}
	
	
	/**
	 * Gets an ID of the elment to be returned, and executes XPath query which the Result which is of type Element, 
	 * i.e.:
	 * <userstory>
	 * 		<id>...</id>
	 * 		<name>...</name>
	 * 		...
	 * </userstory>
	 * @param query
	 * @return
	 * @throws Exception 
	 * @throws Exception
	 */
	public Element getElementByQuery(String xquery) throws Exception{
		if(doc==null){
			throw new Exception("root has not been initialized");
		}
		try {
			XPath path = XPath.newInstance(xquery);
			Element e = (Element)path.selectSingleNode(doc);
			return e;
		} catch (JDOMException e) {
			e.printStackTrace();
			throw e;
		}
	}
	
	/**
	 * Gets an element based on its id
	 * @param id
	 * @return
	 * @throws Exception
	 */
	public Element getElement(String id) throws Exception{
		if(doc==null){
			throw new Exception("root has not been initialized!");
		}
		String xquery = "/descendant::*[id=\"" + id + "\"]";
		try {
			XPath path = XPath.newInstance(xquery);
			Element e = (Element)path.selectSingleNode(doc);
			return e;
		} catch (JDOMException e) {
			e.printStackTrace();
			throw e;
		}
	}
	
	public String getElementTextByTagname(String tagname) throws Exception {
		if(doc==null){
			throw new Exception("root has not been initialized!");
		}
		String xquery = "/descendant::" + tagname;
		try {
			XPath path = XPath.newInstance(xquery);
			Element element = (Element)path.selectSingleNode(doc);
			return element.getText();
		} catch (JDOMException e) {
			e.printStackTrace();
			throw e;
		}
	}
	
	/**
	 * Adds an element to the existing xml-root 
	 * @param element
	 */
	public void addElement(Element parent, Element toAdd){
		parent.addContent(toAdd);
	}
	
	/**
	 * updates an Element with the information passed as element of the
	 * elemnt which has the id passed as parameter
	 * e.g. element=<name>New Name</name>, id="1.1.1"
	 * @param element content of the Element which has to be updated
	 * @param id identifier of the element in the XML file to update
	 * @throws Exception 
	 */
	public void updateElement(Element element, String id) throws Exception{
		Element toUpdate = getElement(id);
		if(toUpdate == null) {
			throw new Exception("Error, couldn't find object to update with id " + id);
		}
		toUpdate = toUpdate.getChild(element.getName());
		toUpdate.setText(element.getText());
	}
	
	/**
	 * Removes an element from the xml-file. It uses the getElement(..) method
	 * to retrieve a reference of, gets its parent and then removes it.
	 * @param id identifier of the element to delete
	 * @throws Exception 
	 */
	public void removeParent(String id) throws Exception{
		if(doc==null){
			throw new Exception("root has not been initialized!");
		}
		
		Element toDelete = getElement(id);
		if(toDelete==null){
			throw new Exception("Error, couldn't found object to delete with id " + id);
		}
		Element parentOfToDelete = toDelete.getParentElement();
		parentOfToDelete.removeContent(toDelete);
	}
	
	
	/**
	 * Removes an element from the xml-file. It uses the getElement(..) method
	 * to retrieve a reference of, gets its parent and then removes it.
	 * @param id identifier of the element to delete
	 * @throws Exception 
	 */
	@SuppressWarnings("unchecked")
	public void removeElement(String id) throws Exception{
		if(doc==null){
			throw new Exception("root has not been initialized!");
		}
		
		Element parent = getElementByQuery("/descendant::developers");
		if(parent==null){
			throw new Exception("Error, couldn't found object to delete with id " + id);
		}
		List<Element> children = parent.getChildren();
		for (Element element : children) {
			if (element.getText().equals(id)){
				parent.removeContent(element);
				break;
			}
		}
	}
	
	/**
	 * Writes the xml-tree contained in the Document object to the file,
	 * exactly to the file specified in the variable "xmlFile"
	 * @throws IOException 
	 *
	 */
	public void writeToFile() throws IOException{
		try{
			Format f = Format.getPrettyFormat();
			f.setEncoding("ISO-8859-1");
			XMLOutputter outputter = new XMLOutputter(f);
			FileWriter writer = new FileWriter(xmlFile);
			outputter.output(doc,writer);
			writer.close();
		}catch(IOException e){
			throw e;
		}
	}	
}
